/*
 * board.cc
 * This file is part of Quarto
 *
 * Copyright (C) 2012 - Ian Liu Rodrigues <ian.liu88@gmail.com>
 *
 * Quarto is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Quarto is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Quarto. If not, see <http://www.gnu.org/licenses/>.
 */

#include "board.hpp"

using namespace br_unicamp;

Board::Board(void)
{
  for (int i = 0; i < 16; i++)
    places[i] = 0;
}

Board::~Board(void)
{
  for (int i = 0; i < 16; i++)
    if (places[i])
      delete places[i];
}

bool Board::gameEnded(void) const
{
  bool isFull = true;
  for (int i = 0; i < 16; i++)
    if (places[i] == 0)
      isFull = false;

  if (isFull)
    return true;

  return haveWinner();
}

bool Board::gameDraw(void) const
{
  return !haveWinner();
}

static bool areFourSimilar(const Piece* four[4])
{
  unsigned char a, b, c, d, x;

  for (int i = 0; i < 4; i++)
    if (four[i] == 0)
      return false;

  a = four[0]->getFlags();
  b = four[1]->getFlags();
  c = four[2]->getFlags();
  d = four[3]->getFlags();

  x = (a ^ b) | (a ^ c) | (a ^ d)
      | (b ^ c) | (b ^ d) | (c ^ d);

  return (x & 0xf) != 0xf;
}

bool Board::haveWinner(void) const
{
  const Piece *four[4];
  bool victory = areFourSimilar(getBoardRow(0, four))
      || areFourSimilar(getBoardRow(1, four))
      || areFourSimilar(getBoardRow(2, four))
      || areFourSimilar(getBoardRow(3, four))
      || areFourSimilar(getBoardCol(0, four))
      || areFourSimilar(getBoardCol(1, four))
      || areFourSimilar(getBoardCol(2, four))
      || areFourSimilar(getBoardCol(3, four))
      || areFourSimilar(getBoardDiag(0, four))
      || areFourSimilar(getBoardDiag(1, four));
  return victory;
}

void Board::markPlace(int pos, const Piece& piece) {
  if (pos < 0 || pos >= 16)
    return;

  if (places[pos] != 0)
    return;

  places[pos] = new Piece(piece);
}

const Piece* Board::get(int pos) const {
  if (pos < 0 || pos >= 16)
    return 0;
  return places[pos];
}

const Piece** Board::getBoardRow(int row, const Piece* four[4]) const
{
  if (row < 0 || row > 3)
    return 0;

  for (int i = 0; i < 4; i++)
    four[i] = places[row * 4 + i];
  return four;
}

const Piece** Board::getBoardCol(int col, const Piece* four[4]) const
{
  if (col < 0 || col > 3)
    return 0;

  for (int i = 0; i < 4; i++)
    four[i] = places[i * 4 + col];
  return four;
}

const Piece** Board::getBoardDiag(int diag, const Piece* four[4]) const
{
  if (diag == 0) {
    for (int i = 0; i < 4; i++)
      four[i] = places[i * 4 + i];
  } else {
    for (int i = 0; i < 4; i++)
      four[i] = places[i * 4 + 3 - i];
  }
  return four;
}
